package co.fitstart.coach.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.BusinessException;
import com.demo2do.core.persistence.GenericDaoSupport;
import com.demo2do.core.persistence.annotation.Dynamic;
import com.demo2do.core.utils.DateUtils;

import co.fitstart.coach.dao.CoursewareDao;
import co.fitstart.coach.dao.CoursewareTemplateDao;
import co.fitstart.coach.entity.courseware.DietaryRequest;
import co.fitstart.coach.entity.courseware.PlannedScheduleRequest;
import co.fitstart.coach.handler.SmsSender;
import co.fitstart.coach.service.WeeklyPlanService;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.course.schedule.ScheduleStatus;
import co.fitstart.entity.courseware.dietary.Dietary;
import co.fitstart.entity.courseware.dietary.DietaryProgram;
import co.fitstart.entity.courseware.dietary.DietaryProgramTemplate;
import co.fitstart.entity.courseware.dietary.DietaryProgramType;
import co.fitstart.entity.courseware.exercise.Exercise;
import co.fitstart.entity.courseware.exercise.ExerciseContent;
import co.fitstart.entity.courseware.exercise.LissType;
import co.fitstart.entity.courseware.exercise.PlannedSchedule;
import co.fitstart.entity.courseware.fitnessgroup.BatchCourseware;
import co.fitstart.entity.courseware.fitnessgroup.BatchCoursewareTemplate;
import co.fitstart.entity.courseware.fitnessgroup.BatchCoursewareType;

/**
 * 
 * @author haiyan_xiao
 *
 */
@Service("weeklyPlanService")
public class WeeklyPlanServiceImpl implements WeeklyPlanService {

    @Autowired
    private GenericDaoSupport genericDaoSupport;

    @Dynamic
    private CoursewareTemplateDao coursewareTemplateDao;

    @Dynamic
    private CoursewareDao coursewareDao;

    @Autowired
    private SmsSender smsSender;

    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#loadDietary(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    public Dietary loadDietary(BatchCourseware batchCourseware) {
        return genericDaoSupport.searchForObject("FROM Dietary WHERE disabled = false"
                + " AND batch = :batch", "batch", batchCourseware, Dietary.class);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#generallyLoadDietary(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    public Dietary generallyLoadDietary(BatchCourseware batchCourseware) {
        Dietary dietary = this.loadDietary(batchCourseware);
        
        if(dietary != null && (StringUtils.isNotBlank(dietary.getBreakfastDescription()) || StringUtils.isNotBlank(dietary.getBreakfastSnackDescription())
                || StringUtils.isNotBlank(dietary.getLunchDescription()) || StringUtils.isNotBlank(dietary.getLunchSnackDescription())
                || StringUtils.isNotBlank(dietary.getDinnerDescription()) || StringUtils.isNotBlank(dietary.getDinnerSnackDescription())
                || StringUtils.isNotBlank(dietary.getWaterIntake()) || StringUtils.isNotBlank(dietary.getRemark()))) {
            return dietary;
        } else if(batchCourseware.getDietaryPrograms() != null && !batchCourseware.getDietaryPrograms().isEmpty()) {
            Map<String, DietaryProgram> dietaryPrograms = batchCourseware.getDietaryPrograms();
            
            DietaryProgramTemplate breakfastTemplate = null;
            String breakfastDescription = null;
            DietaryProgramTemplate breakfastSnackTemplate = null;
            String breakfastSnackDescription = null;
            if(dietaryPrograms.get(DietaryProgramType.BREAKFAST) != null) {
                breakfastTemplate = dietaryPrograms.get(DietaryProgramType.BREAKFAST).getTemplate();
                breakfastDescription = dietaryPrograms.get(DietaryProgramType.BREAKFAST).getDescription();
                
                if(dietaryPrograms.get(DietaryProgramType.BREAKFAST).getSnacks() != null) {
                    DietaryProgram breakfastSnack = genericDaoSupport.load(DietaryProgram.class, dietaryPrograms.get(DietaryProgramType.BREAKFAST).getSnacks());
                    
                    if(breakfastSnack != null) {
                        breakfastSnackTemplate = breakfastSnack.getTemplate();
                        breakfastSnackDescription = breakfastSnack.getDescription();
                    }
                }
            }
            
            DietaryProgramTemplate lunchTemplate = null;
            String lunchDescription = null;
            DietaryProgramTemplate lunchSnackTemplate = null;
            String lunchSnackDescription = null;
            if(dietaryPrograms.get(DietaryProgramType.LUNCH) != null) {
                lunchTemplate = dietaryPrograms.get(DietaryProgramType.LUNCH).getTemplate();
                lunchDescription = dietaryPrograms.get(DietaryProgramType.LUNCH).getDescription();
                
                if(dietaryPrograms.get(DietaryProgramType.LUNCH).getSnacks() != null) {
                    DietaryProgram lunchSnack = genericDaoSupport.load(DietaryProgram.class, dietaryPrograms.get(DietaryProgramType.LUNCH).getSnacks());
                    
                    if(lunchSnack != null) {
                        lunchSnackTemplate = lunchSnack.getTemplate();
                        lunchSnackDescription = lunchSnack.getDescription();
                    }
                }
            }
            
            DietaryProgramTemplate dinnerTemplate = null;
            String dinnerDescription = null;
            DietaryProgramTemplate dinnerSnackTemplate = null;
            String dinnerSnackDescription = null;
            if(dietaryPrograms.get(DietaryProgramType.DINNER) != null) {
                dinnerTemplate = dietaryPrograms.get(DietaryProgramType.DINNER).getTemplate();
                dinnerDescription = dietaryPrograms.get(DietaryProgramType.DINNER).getDescription();
                
                if(dietaryPrograms.get(DietaryProgramType.DINNER).getSnacks() != null) {
                    DietaryProgram dinnerSnack = genericDaoSupport.load(DietaryProgram.class, dietaryPrograms.get(DietaryProgramType.DINNER).getSnacks());
                    
                    if(dinnerSnack != null) {
                        dinnerSnackTemplate = dinnerSnack.getTemplate();
                        dinnerSnackDescription = dinnerSnack.getDescription();
                    }
                }
            }
            
            if(dietary == null) {
                dietary = new Dietary(batchCourseware);
                genericDaoSupport.save(dietary.edit(breakfastTemplate, breakfastDescription, breakfastSnackTemplate, breakfastSnackDescription,
                        lunchTemplate, lunchDescription, lunchSnackTemplate, lunchSnackDescription,
                        dinnerTemplate, dinnerDescription, dinnerSnackTemplate, dinnerSnackDescription, null, null));
            } else {
                genericDaoSupport.update(dietary.edit(breakfastTemplate, breakfastDescription, breakfastSnackTemplate, breakfastSnackDescription,
                        lunchTemplate, lunchDescription, lunchSnackTemplate, lunchSnackDescription,
                        dinnerTemplate, dinnerDescription, dinnerSnackTemplate, dinnerSnackDescription, null, null));
            }
        } else {
            BatchCoursewareTemplate batchCoursewareTemplate = coursewareTemplateDao.loadBatchCoursewareTemplate(batchCourseware.getCourseRound().getFitnessGroup(), BatchCoursewareType.WEEKLY, batchCourseware.getSorting());
            
            DietaryProgramTemplate breakfastTemplate = batchCoursewareTemplate.getBreakfast();
            String breakfastDescription = null;
            DietaryProgramTemplate breakfastSnackTemplate = null;
            String breakfastSnackDescription = null;
            if(breakfastTemplate != null) {
                breakfastDescription = breakfastTemplate.getDescription();
                
                if(breakfastTemplate.getSnacks() != null) {
                    breakfastSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, breakfastTemplate.getSnacks());
                    
                    if(breakfastSnackTemplate != null) {
                        breakfastSnackDescription = breakfastSnackTemplate.getDescription();
                    }
                }
            }
            
            DietaryProgramTemplate lunchTemplate = batchCoursewareTemplate.getLunch();
            String lunchDescription = null;
            DietaryProgramTemplate lunchSnackTemplate = null;
            String lunchSnackDescription = null;
            if(lunchTemplate != null) {
                lunchDescription = lunchTemplate.getDescription();
                
                if(lunchTemplate.getSnacks() != null) {
                    lunchSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, lunchTemplate.getSnacks());
                    
                    if(lunchSnackTemplate != null) {
                        lunchSnackDescription = lunchSnackTemplate.getDescription();
                    }
                }
            }
            
            DietaryProgramTemplate dinnerTemplate = batchCoursewareTemplate.getDinner();
            String dinnerDescription = null;
            DietaryProgramTemplate dinnerSnackTemplate = null;
            String dinnerSnackDescription = null;
            if(dinnerTemplate != null) {
                dinnerDescription = dinnerTemplate.getDescription();
                
                if(dinnerTemplate.getSnacks() != null) {
                    dinnerSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dinnerTemplate.getSnacks());
                    
                    if(dinnerSnackTemplate != null) {
                        dinnerSnackDescription = dinnerSnackTemplate.getDescription();
                    }
                }
            }
            
            if(dietary == null) {
                dietary = new Dietary(batchCourseware);
                genericDaoSupport.save(dietary.edit(breakfastTemplate, breakfastDescription, breakfastSnackTemplate, breakfastSnackDescription,
                        lunchTemplate, lunchDescription, lunchSnackTemplate, lunchSnackDescription,
                        dinnerTemplate, dinnerDescription, dinnerSnackTemplate, dinnerSnackDescription, null, null));
            } else {
                genericDaoSupport.update(dietary.edit(breakfastTemplate, breakfastDescription, breakfastSnackTemplate, breakfastSnackDescription,
                        lunchTemplate, lunchDescription, lunchSnackTemplate, lunchSnackDescription,
                        dinnerTemplate, dinnerDescription, dinnerSnackTemplate, dinnerSnackDescription, null, null));
            }
        }
        
        return dietary;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#editDietary(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, co.fitstart.coach.entity.courseware.DietaryRequest)
     */
    public void editDietary(BatchCourseware batchCourseware, DietaryRequest dietary) {
        Dietary originalDietary = this.loadDietary(batchCourseware);
        
        DietaryProgramTemplate breakfastTemplate = null;
        if(dietary.getBreakfastTemplateId() != null) {
            breakfastTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getBreakfastTemplateId());
        }
        
        DietaryProgramTemplate breakfastSnackTemplate = null;
        if(dietary.getBreakfastSnackTemplateId() != null) {
            breakfastSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getBreakfastSnackTemplateId());
        }
        
        DietaryProgramTemplate lunchTemplate = null;
        if(dietary.getLunchTemplateId() != null) {
            lunchTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getLunchTemplateId());
        }
        
        DietaryProgramTemplate lunchSnackTemplate = null;
        if(dietary.getLunchSnackTemplateId() != null) {
            lunchSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getLunchSnackTemplateId());
        }
        
        DietaryProgramTemplate dinnerTemplate = null;
        if(dietary.getDinnerTemplateId() != null) {
            dinnerTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getDinnerTemplateId());
        }
        
        DietaryProgramTemplate dinnerSnackTemplate = null;
        if(dietary.getDinnerSnackTemplateId() != null) {
            dinnerSnackTemplate = genericDaoSupport.load(DietaryProgramTemplate.class, dietary.getDinnerSnackTemplateId());
        }
        
        
        if(originalDietary == null) {
            Dietary newDietary = new Dietary(batchCourseware);
            
            genericDaoSupport.save(newDietary.edit(breakfastTemplate, dietary.getBreakfastDescription(),
                    breakfastSnackTemplate, dietary.getBreakfastSnackDescription(),
                    lunchTemplate, dietary.getLunchDescription(),
                    lunchSnackTemplate, dietary.getLunchSnackDescription(),
                    dinnerTemplate, dietary.getDinnerDescription(),
                    dinnerSnackTemplate, dietary.getDinnerSnackDescription(),
                    dietary.getWaterIntake(), dietary.getRemark()));
        } else {
            genericDaoSupport.update(originalDietary.edit(breakfastTemplate, dietary.getBreakfastDescription(),
                    breakfastSnackTemplate, dietary.getBreakfastSnackDescription(),
                    lunchTemplate, dietary.getLunchDescription(),
                    lunchSnackTemplate, dietary.getLunchSnackDescription(),
                    dinnerTemplate, dietary.getDinnerDescription(),
                    dinnerSnackTemplate, dietary.getDinnerSnackDescription(),
                    dietary.getWaterIntake(), dietary.getRemark()));
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#loadExercise(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    public Exercise loadExercise(BatchCourseware batchCourseware) {
        return genericDaoSupport.searchForObject("FROM Exercise WHERE disabled = false"
                + " AND batch = :batch", "batch", batchCourseware, Exercise.class);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#listPlannedSchedule(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware)
     */
    @SuppressWarnings("unchecked")
    public List<PlannedSchedule> listPlannedSchedule(BatchCourseware batchCourseware) {
        return genericDaoSupport.searchForList("FROM PlannedSchedule WHERE disabled = false"
                + " AND batch = :batch AND content IS NOT NULL ORDER BY day ASC", "batch", batchCourseware);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#listSchedule(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, java.util.List)
     */
    @SuppressWarnings("unchecked")
    public Map<Integer, CourseSchedule> listSchedule(BatchCourseware batchCourseware, List<PlannedSchedule> schedules) {
        if(schedules.isEmpty()) {
            return null;
        } else {
            int begin = 0;
            if(batchCourseware.getSorting() > 1) {
                Map<String, Object> parameters = new HashMap<>();
                parameters.put("courseRound", batchCourseware.getCourseRound());
                parameters.put("sorting", batchCourseware.getSorting());
                
                begin = genericDaoSupport.count("FROM PlannedSchedule WHERE disabled = false"
                        + " AND batch.courseRound = :courseRound AND batch.sorting < :sorting"
                        + " AND content IS NOT NULL", parameters);
            }
            
            Map<String, Object> parameters = new HashMap<>();
            parameters.put("courseRound", batchCourseware.getCourseRound());
            parameters.put("schedule", ScheduleStatus.SCHEDULE);
            
            List<CourseSchedule> courseSchedules = genericDaoSupport.searchForList("FROM CourseSchedule WHERE disabled = false"
                    + " AND courseRound = :courseRound AND scheduleStatus != :schedule ORDER BY occurDate ASC,occurHour ASC",
                    parameters, begin, schedules.size());
            
            if(courseSchedules.isEmpty()) {
                return null;
            } else {
                Map<Integer, CourseSchedule> map = new HashMap<>();
                
                for(int i=0; i <courseSchedules.size(); i++) {
                    map.put(schedules.get(i).getDay(), courseSchedules.get(i));
                }
                
                return map;
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#editExercise(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, co.fitstart.entity.courseware.exercise.Exercise, co.fitstart.coach.entity.courseware.PlannedScheduleRequest, java.util.List)
     */
    public void editExercise(BatchCourseware batchCourseware, Exercise exercise,
            PlannedScheduleRequest plannedSchedules, List<LissType> lissTypes) {
        Exercise originalExercise = this.loadExercise(batchCourseware);
        
        if(originalExercise == null) {
            if((lissTypes != null && lissTypes.size() > 0) || StringUtils.isNotBlank(exercise.getOtherLiss())
                    || StringUtils.isNotBlank(exercise.getHit()) || StringUtils.isNotBlank(exercise.getCore())
                    || StringUtils.isNotBlank(exercise.getRemark()) || exercise.getHomeworkFrequency() != null
                    || exercise.getScheduleFrequency() != null) {
                genericDaoSupport.save(exercise.initialize(batchCourseware).editLissType(lissTypes));
            }
        } else {
            genericDaoSupport.update(originalExercise.edit(exercise).editLissType(lissTypes));
        }
        
        Map<Integer, List<ExerciseContent>> map = plannedSchedules.toMap();
        
        for(int i=1; i<=7; i++) {
            PlannedSchedule originalSchedule  = this.loadPlannedSchedule(batchCourseware, i);
            
            if(originalSchedule == null) {
                if(map.get(i) != null && map.get(i).size() > 0) {
                    PlannedSchedule schedule = new PlannedSchedule(batchCourseware, i);
                    
                    genericDaoSupport.save(schedule.editContent(map.get(i)));
                }
            } else {
                genericDaoSupport.update(originalSchedule.editContent(map.get(i)));
            }
        }
    }
    
    /**
     * load PlannedSchedule by batchCourseware & day
     * @param batchCourseware
     * @param day
     * @return
     */
    private PlannedSchedule loadPlannedSchedule(BatchCourseware batchCourseware, int day) {
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("batch", batchCourseware);
        parameters.put("day", day);
        
        return genericDaoSupport.searchForObject("FROM PlannedSchedule WHERE disabled = false"
                + " AND batch = :batch AND day = :day", parameters, PlannedSchedule.class);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#linkWeeklyPlan(co.fitstart.entity.courseware.fitnessgroup.BatchCourseware, java.lang.String)
     */
    public void linkWeeklyPlan(BatchCourseware batchCourseware, String coachTips) {
        Dietary dietary = this.loadDietary(batchCourseware);
        if(dietary == null || (StringUtils.isBlank(dietary.getBreakfastDescription()) && StringUtils.isBlank(dietary.getLunchDescription()) && StringUtils.isBlank(dietary.getDinnerDescription()))) {
            throw new BusinessException("早午餐为空不能发送");
        } else {
            Exercise exercise = this.loadExercise(batchCourseware);
            if(exercise == null || (exercise.getHomeworkFrequency() == null && exercise.getLissTypeList().isEmpty() && StringUtils.isBlank(exercise.getOtherLiss()) && StringUtils.isBlank(exercise.getHit()) && StringUtils.isBlank(exercise.getCore()) && StringUtils.isBlank(exercise.getRemark()))) {
                throw new BusinessException("家庭作业为空不能发送");
            } else if(exercise.getScheduleFrequency() == null) {
                List<PlannedSchedule> schedules = this.listPlannedSchedule(batchCourseware);
                if(schedules.isEmpty()) {
                    throw new BusinessException("门店训练为空不能发送");
                } else {
                    int flag = 0;
                    for(PlannedSchedule schedule : schedules) {
                        if(!schedule.getContentList().isEmpty()) {
                            flag = 1;
                        }
                    }
                    
                    if(flag == 0) {
                        throw new BusinessException("门店训练为空不能发送");
                    }
                }
            }
        }
        
        genericDaoSupport.update(batchCourseware.link("batch_courseware_link", coachTips));
        
        // send message to student
        smsSender.sendCoursewareNotice(batchCourseware.getCourseRound().getStudent().getMobile());
        
        this.finishCourseRound(batchCourseware.getCourseRound(), "weekly", 1);
    }
    
    /**
     * 
     * @param courseRound
     * @param type
     * @param amount
     */
    private void finishCourseRound(CourseRound courseRound, String type, int amount) {
        courseRound.editPlannedAmount(type, amount);
        
        if(courseRound.getCourseType().isIndividual() && !courseRound.getCourse().isExperience()) {
            if(courseRound.getPlannedWeekly() == courseRound.getFitnessGroup().getWeeks()) {
                courseRound.finishCourseware();
            }
        } else {
            List<BatchCourseware> batchCoursewares = coursewareDao.listBatchCourseware(courseRound);
            if(batchCoursewares.isEmpty()) {
                courseRound.finishCourseware();
            }
        }
        
        genericDaoSupport.update(courseRound);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#getPlannedSchedule(co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public PlannedSchedule getPlannedSchedule(CourseSchedule courseSchedule) {
        int rank = coursewareDao.getRank(courseSchedule.getCourseRound(),
                new ScheduleStatus[]{ScheduleStatus.READY, ScheduleStatus.EXPIRY, ScheduleStatus.FINISH},
                courseSchedule.getOccurDate(), courseSchedule.getOccurHour());
        
        List<PlannedSchedule> plannedSchedules = coursewareDao.getPlannedSchedule(courseSchedule.getCourseRound(), rank - 1, 1);
        return plannedSchedules.isEmpty() ? null : plannedSchedules.get(0);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#getCurrentWeek(co.fitstart.entity.course.round.CourseRound)
     */
    public int getCurrentWeek(CourseRound courseRound) {
        Date now = new Date();
        
        if(courseRound.getEffectiveStart() == null || courseRound.getEffectiveStart().after(now)) {
            return 1;
        } else {
            int week = (int)((now.getTime() - courseRound.getEffectiveStart().getTime())/1000/60/60/24/7) + 1;
            
            if(courseRound.getFitnessGroup() != null && week > courseRound.getFitnessGroup().getWeeks()) {
                week = courseRound.getFitnessGroup().getWeeks();
            }
            
            return week;
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.coach.service.WeeklyPlanService#getWeekBeginAndEnd(co.fitstart.entity.course.round.CourseRound, int)
     */
    public Map<String, Date> getWeekBeginAndEnd(CourseRound courseRound, int week) {
        Map<String, Date> result = new HashMap<>();
        
        if(courseRound.getEffectiveStart() != null) {
            result.put("begin", DateUtils.addWeeks(courseRound.getEffectiveStart(), week - 1));
            result.put("end", DateUtils.addDays(courseRound.getEffectiveStart(), week * 7 - 1));
        }
        
        return result;
    }
}
